class Ranker(object):
    def __init__(self, 
                 vote_count_field='vote_count', 
                 vote_sum_field='vote_sum', 
                 *args,
                 **kwargs):

        self.vote_count_field = vote_count_field
        self.vote_sum_field = vote_sum_field
        
        if 'extras' in kwargs:
            self.extras = kwargs['extras']
        else:
            self.extras = None

    def generate_criteria(self):
        fields = {
            'vote_count': self.vote_count_field,
            'vote_sum': self.vote_sum_field
            }

        if self.extras:
            for k, v in self.extras.iteritems():
                fields[k] = v

        rank_criteria = self.rank_criteria % fields

        return rank_criteria


class AvgRanker(Ranker):
    rank_criteria = """
       (`%(vote_sum)s`/`%(vote_sum)s`)
    """

    def __init__(self, *args, **kwargs):
        Ranker.__init__(self, *args, **kwargs)


class HackerNewsRanker(Ranker):
    """
    Rank a group of objects with the
    Hacker News (YC) algorithm
    
    Original:
    (p - 1) / (t + 2)^1.5
    p = vote count - 1 (to offset submitter vote)
    t = time since submission, in hours
    
    Modified:
    p / (t + 2)^1.5
    
    sum of all votes
    divided by
    (hours since creation + 2) ^ 1.5
    
    p = sum of all votes
    s = vote count
    t = time since submission in hours
    """
    rank_criteria = """
        (
            (`%(vote_sum)s`) / pow((((((unix_timestamp(now())-unix_timestamp(`%(created_date)s`))/60)/60)+2)), 1.5)
        )
    """

    def __init__(self, *args, **kwargs):
        kwargs['extras'] = {
            'created_date': 'created_date'
            }

        Ranker.__init__(self, *args, **kwargs)


class BayesianRanker(Ranker):
    rank_criteria = """
        (`%(vote_sum)s` / `%(vote_count)s`),
        (
            (%(weight)s) + (`%(vote_count)s` * (`%(vote_sum)s` / `%(vote_count)s`))
        ) / (%(avg_num_votes)s + `%(vote_count)s`)
    """

    def __init__(self, *args, **kwargs):
        def avg(l):
            return sum(l) / len(l)

        vs_model = kwargs['vs_model']
        ttl_votes = vs_model.objects.filter().values('vote_count', 
                                                     'vote_sum')

        avg_num_votes = avg([vs['vote_count'] for vs in ttl_votes])
        avg_rating = avg([vs['vote_sum'] for vs in ttl_votes])
        weight = avg_num_votes * avg_rating

        kwargs['extras'] = {
            'weight': weight,
            'avg_num_votes': avg_num_votes
            }

        Ranker.__init__(self, *args, **kwargs)


class RedditRanker(Ranker):
    pass
